#include "os_common.h"

#ifdef NDIS51_MINIPORT
VOID
WBNDIS_PnPNotify( IN PADAPTER Adapter, IN NDIS_DEVICE_PNP_EVENT PnPEvent, IN PVOID InformationBuffer,	IN ULONG InformationBufferLength )
{
	#ifdef _PE_STATE_DUMP_
	WBDEBUG(("====> MPPnPEventNotify\n"));
	#endif

	switch (PnPEvent)
	{
		case NdisDevicePnPEventQueryRemoved:
			#ifdef _PE_STATE_DUMP_
			WBDEBUG(("Wb32PnPNotify: NdisDevicePnPEventQueryRemoved\n"));
			#endif
			break;

		case NdisDevicePnPEventRemoved:
			#ifdef _PE_STATE_DUMP_
			WBDEBUG(("Wb32PnPNotify: NdisDevicePnPEventRemoved\n"));
			#endif
			break;       

		case NdisDevicePnPEventSurpriseRemoved:
			#ifdef _PE_STATE_DUMP_
			WBDEBUG(("Wb32PnPNotify: NdisDevicePnPEventSurpriseRemoved\n"));
			#endif
            break;

		case NdisDevicePnPEventQueryStopped:
			#ifdef _PE_STATE_DUMP_
			WBDEBUG(("Wb32PnPNotify: NdisDevicePnPEventQueryStopped\n"));
			#endif
			break;

		case NdisDevicePnPEventStopped:
			#ifdef _PE_STATE_DUMP_
			WBDEBUG(("Wb32PnPNotify: NdisDevicePnPEventStopped\n"));
			#endif
			break;      
            
		case NdisDevicePnPEventPowerProfileChanged:
//			WBDEBUG(("Wb32PnPNotify: NdisDevicePnPEventPowerProfileChanged\n"));
			#ifdef _PE_STATE_DUMP_
			if( *(PULONG)InformationBuffer == NdisPowerProfileAcOnLine )
				WBDEBUG(("Wb32PnPNotify: NdisPowerProfileAcOnLine\n"));
			if( *(PULONG)InformationBuffer == NdisPowerProfileBattery )
				WBDEBUG(("Wb32PnPNotify: NdisPowerProfileBattery\n"));
			#endif
			break;      
            
		default:
			#ifdef _PE_STATE_DUMP_
			WBDEBUG(("Wb32PnPNotify: unknown PnP event %x \n", PnPEvent));
			#endif
			break;         
	}
}
#endif

//===========================================================================
//  WBNDIS_SendPackets  --
//
//  Description:
//    The WBNDIS_SendPackets request instructs a driver to transmit packets
//    through the adapter onto the medium.
//
//  Arguments:
//    MiniportAdapterContext - Context registered with the wrapper, really
//                             a pointer to the adapter.
//    PacketArray            - A pointer to the packet array that is to be
//                             transmitted.
//    NumberOfPackets        - The number of pointers to packet descriptors
//                             at PacketArray.
//
//===========================================================================
VOID
WBNDIS_SendPackets( IN NDIS_HANDLE   MiniportAdapterContext,
					IN PPNDIS_PACKET PacketArray,
					IN UINT          NumberOfPackets )
{
	PADAPTER		Adapter = (PADAPTER)MiniportAdapterContext;
	PWBNDIS			pWbNdis = &Adapter->WbNdis;
	PNDIS_PACKET	pNdisPacket;
	PNDIS_BUFFER	pNdisPacketBuffer;
	PUCHAR			pBufAddress;
	ULONG			i;
	UINT			NdisPacketBufferCount, NdisPacketSize, buf_size;


	//
	// Fill information into reserved space
	//
	for( i=0; i<NumberOfPackets; i++ )
	{
		pNdisPacket = PacketArray[i];
		NdisQueryPacket( pNdisPacket, NULL, &NdisPacketBufferCount, &pNdisPacketBuffer, &NdisPacketSize);

		//931130.5.q
		if( !NdisPacketSize )
		{
			#ifdef _PE_TX_DUMP_
			WBDEBUG(("Ndis packet error [%d] = %d\n", NdisPacketBufferCount, NdisPacketSize ));
			#endif
			NDIS_SET_PACKET_STATUS( pNdisPacket, NDIS_STATUS_FAILURE );
			NdisMSendComplete( Adapter->WbNdis.MiniportAdapterHandle, pNdisPacket, NDIS_STATUS_FAILURE);
			#ifdef _PE_TX_DUMP_
			WBDEBUG(("!NdisPacketSize || (NdisPacketBufferCount>(MAX_DESCRIPTOR_BUFFER_INDEX-2))\n"));
			#endif
		}
		else
		{
			NDIS_SET_PACKET_STATUS( pNdisPacket, NDIS_STATUS_PENDING );

			// Modify the follow for the number of buffer is larger than MAX_DESCRIPTOR_BUFFER_INDEX
			// WBNDIS_GetNextPacket will copy the packet buffer into transmitting buffer directly
			RESERVED(pNdisPacket)->pNdisPacketBuffer = NULL;
			if( !(NdisPacketBufferCount>(MAX_DESCRIPTOR_BUFFER_INDEX-2)) )
				RESERVED(pNdisPacket)->pNdisPacketBuffer = pNdisPacketBuffer;
			RESERVED(pNdisPacket)->NdisPacketSize = (USHORT)NdisPacketSize;

			// Frame type acknowledgement
			// //931130.5.r
			#ifdef NDIS51_MINIPORT
			NdisQueryBufferSafe( pNdisPacketBuffer, (PVOID *) &pBufAddress, &buf_size, HighPagePriority );
			#else
			NdisQueryBuffer( pNdisPacketBuffer, (PVOID *) &pBufAddress, &buf_size );
			#endif
/*
			//[WK DUMP]
			WBDEBUG(("New packet =%x\n", pNdisPacket));
			#ifdef _PE_TX_DUMP_
			{
				PNDIS_BUFFER	pPacketBuffer;
				PUCHAR			pBufAddr;
				UINT			PacketSize, buf_size1;

				//[WK DUMP]
				pPacketBuffer = pNdisPacketBuffer;
				PacketSize = NdisPacketSize;
				WBDEBUG(("=== DUMP 802.3 TX Buff ===\n"));
				WBDEBUG(("Size =%d\n", PacketSize));
				while( pPacketBuffer )
				{
					// Get the address and length of the first buffer
					#ifdef NDIS51_MINIPORT// PD43 20030430 920428.3
					NdisQueryBufferSafe( pPacketBuffer, (PVOID *) &pBufAddr, &buf_size1, HighPagePriority );
					#else
					NdisQueryBuffer( pPacketBuffer, (PVOID *) &pBufAddr, &buf_size1 );
					#endif
					DataDmp( pBufAddr, buf_size1, 0);
					NdisGetNextBuffer( pPacketBuffer, &pPacketBuffer);
				}
			}
			#endif
*/
			//
			// Can be sent now?
			//
			if( pWbNdis->shutdown || !CURRENT_LINK_ON ||
				(
				#ifdef _WPA2_
				CURRENT_DESIRED_WPA2_ENABLE &&
				#endif //end def _WPA2_
				  CURRENT_DESIRED_WPA_ENABLE && CURRENT_CONTROL_PORT_BLOCK && (*(PUSHORT)(pBufAddress+12) != cpu_to_le16(0x8e88)) ) || \
				((psLOCAL->RadioOffStatus.boHwRadioOff == TRUE) || (psLOCAL->RadioOffStatus.boSwRadioOff == TRUE)) )
			{
				NDIS_SET_PACKET_STATUS( pNdisPacket, NDIS_STATUS_FAILURE );
				NdisMSendComplete( Adapter->WbNdis.MiniportAdapterHandle, pNdisPacket, NDIS_STATUS_FAILURE);
				#ifdef _PE_TX_DUMP_
				WBDEBUG(("CURRENT_LINK_OFF + Block\n"));
				#endif
			}
			else
			{
				//
				// Queuing the ndis packet
				//
				OS_SPIN_LOCK_ACQUIRED( &pWbNdis->SpinLock );
				RESERVED(pNdisPacket)->Next = NULL;

				if( pWbNdis->first_packet==NULL)
					pWbNdis->first_packet = pNdisPacket;
				else
					RESERVED(pWbNdis->last_packet)->Next = pNdisPacket;

				pWbNdis->last_packet = pNdisPacket;
				Adapter->sLocalPara._NumTxMSDU++;
				OS_SPIN_LOCK_RELEASED( &pWbNdis->SpinLock );
			}
		}
	}

	//
	// Start to transmit
	//
	Mds_Tx( Adapter );
}


//931130.5.s
VOID
WBNDIS_GetNextPacket( IN PADAPTER Adapter, IN PDESCRIPTOR pDes )
{
	PWBNDIS			pWbNdis = &Adapter->WbNdis;
	PNDIS_PACKET	pNdisPacket;
	PNDIS_BUFFER	pNdisPacketBuffer;
	PUCHAR			pBufAddress, pTgtBufAddress;
	UINT			NdisPacketBufferCount, NdisPacketSize, buf_size;
	UCHAR			first_in;


	//Get a Ndis packet to transmit
	OS_SPIN_LOCK_ACQUIRED(&pWbNdis->SpinLock);
	pNdisPacket = pWbNdis->first_packet;
	if( pNdisPacket == pWbNdis->last_packet )
		pWbNdis->last_packet = NULL;
	pWbNdis->first_packet = RESERVED(pWbNdis->first_packet)->Next;
	OS_SPIN_LOCK_RELEASED(&pWbNdis->SpinLock);//Release spinlock

	//================================================================================
	// Fill the related information into descriptor												//
	//================================================================================
	pWbNdis->TxByteCount += RESERVED(pNdisPacket)->NdisPacketSize; // 940803 Add

	pWbNdis->packet_return = pNdisPacket;
	if( RESERVED(pNdisPacket)->pNdisPacketBuffer )
	{
		pNdisPacketBuffer = RESERVED(pNdisPacket)->pNdisPacketBuffer;

		//Add buffer
		first_in = 0;
		while( pNdisPacketBuffer )
		{
			// Get the address and length of the first buffer
			#ifdef NDIS51_MINIPORT// PD43 20030430 920428.3
			NdisQueryBufferSafe( pNdisPacketBuffer, (PVOID *) &pBufAddress, &buf_size, HighPagePriority );
			#else
			NdisQueryBuffer( pNdisPacketBuffer, (PVOID *) &pBufAddress, &buf_size );
			#endif

			#ifdef _PE_TX_DUMP_
			//[WK DUMP]
			//WBDEBUG(("=== DUMP TX PACKET ===\n"));
			//DataDmp( pBufAddress, buf_size, 0);
			#endif

			//931130.5.t
			//		+------------+
			//		| header + 2 |  <-- DA + SA + T/L
			//		+------------+
			//		+-------------------------------+
			//		| data							|
			//		+-------------------------------+
			//					:
			//					:
			//
			if( first_in == 0 )
			{
				//931130.5.u
				if( buf_size < (DOT_3_TYPE_OFFSET+2) )
				{
					#ifdef _PE_RX_DUMP_
					WBDEBUG(("First buffer of Ndis Packet too small\n"));
					#endif
					NDIS_SET_PACKET_STATUS( pNdisPacket, NDIS_STATUS_FAILURE );
					NdisMSendComplete( Adapter->WbNdis.MiniportAdapterHandle, pNdisPacket, NDIS_STATUS_FAILURE);
					pWbNdis->packet_return = NULL;
					return;
				}

				DESCRIPTOR_ADD_BUFFER( pDes, pBufAddress, DOT_3_TYPE_OFFSET+2 );

				buf_size -= (DOT_3_TYPE_OFFSET+2);
				pBufAddress += (DOT_3_TYPE_OFFSET+2);
				first_in = 1;
			}

			if( buf_size )// Add the left data 931130.5.v
				DESCRIPTOR_ADD_BUFFER( pDes, pBufAddress, (USHORT)buf_size );

			//Get the next ndis packet buffer
			NdisGetNextBuffer( pNdisPacketBuffer, &pNdisPacketBuffer);
		}
	}
	else
	{
		// Copy frame into temp buffer due to too many piece of buffer
		NdisQueryPacket( pNdisPacket, NULL, &NdisPacketBufferCount, &pNdisPacketBuffer, &NdisPacketSize);
		pTgtBufAddress = pWbNdis->NdisBufferTmp;

		// Copy buffer
		while( pNdisPacketBuffer )
		{
			// Get the address and length of the first buffer
			#ifdef NDIS51_MINIPORT// PD43 20030430 920428.3
			NdisQueryBufferSafe( pNdisPacketBuffer, (PVOID *) &pBufAddress, &buf_size, HighPagePriority );
			#else
			NdisQueryBuffer( pNdisPacketBuffer, (PVOID *) &pBufAddress, &buf_size );
			#endif

			OS_MEMORY_COPY( pTgtBufAddress, pBufAddress, buf_size );
			pTgtBufAddress += buf_size;

			//Get the next ndis packet buffer
			NdisGetNextBuffer( pNdisPacketBuffer, &pNdisPacketBuffer);
		}

		// Add buffer
		DESCRIPTOR_ADD_BUFFER( pDes, pWbNdis->NdisBufferTmp, DOT_3_TYPE_OFFSET+2 );
		DESCRIPTOR_ADD_BUFFER( pDes, pWbNdis->NdisBufferTmp+DOT_3_TYPE_OFFSET+2, NdisPacketSize - (DOT_3_TYPE_OFFSET+2) );
	}
}

VOID WBNDIS_GetNextPacketCompleted( IN PADAPTER Adapter, IN PDESCRIPTOR pDes )
{
	PWBNDIS pWbNdis = &Adapter->WbNdis;
	PNDIS_PACKET pNdisPacket;

	//
	// Return the send result to NDIS
	//
	pNdisPacket = pWbNdis->packet_return;
	if( !pNdisPacket )
	{
		#ifdef _PE_TX_DUMP_
		WBDEBUG(("WBNDIS Tx null point error\n"));
		#endif
		return;
	}

	//In 2m_wlan_wep of HCT test, if the key is removed, the transmitting data should
	//still return "success". Or this test item will be failed.
	//[WK DUMP]
	//WBDEBUG(("Packet tx completely. addr =%x\n", pNdisPacket));
	NdisMSendComplete( Adapter->WbNdis.MiniportAdapterHandle,
						   pNdisPacket,
						   NDIS_STATUS_SUCCESS );
	MIB_GS_XMIT_OK_INC;
	pWbNdis->packet_return = NULL;
}

VOID
WBNDIS_stop( IN PADAPTER Adapter )
{
	PWBNDIS			pWbNdis = &Adapter->WbNdis;
	PNDIS_PACKET	pNdisPacket;


	if( OS_ATOMIC_INC( Adapter, &pWbNdis->ThreadCount ) == 1 )
	{
		// Shutdown module immediately
		pWbNdis->shutdown = 1;

		// Return all the ndis packet handle in sending queue
		OS_SPIN_LOCK_ACQUIRED(&pWbNdis->SpinLock);
		while( pWbNdis->first_packet != NULL )
		{
			// Update queue point before exit spin-lock protect
			pNdisPacket = pWbNdis->first_packet;
			pWbNdis->first_packet = RESERVED(pWbNdis->first_packet)->Next;
			OS_SPIN_LOCK_RELEASED(&pWbNdis->SpinLock);//release spinlock
			NdisMSendComplete( Adapter->WbNdis.MiniportAdapterHandle,
							   pNdisPacket,
							   NDIS_STATUS_FAILURE );
			OS_SPIN_LOCK_ACQUIRED(&pWbNdis->SpinLock);
		}
		pWbNdis->last_packet = NULL;
		OS_SPIN_LOCK_RELEASED(&pWbNdis->SpinLock);//release spinlock
	}

	OS_ATOMIC_DEC( Adapter, &pWbNdis->ThreadCount );
}

VOID
WBNDIS_Destroy( IN PADAPTER Adapter )
{
	PWBNDIS			pWbNdis = &Adapter->WbNdis;
	ULONG			i;
	BOOLEAN			Cancel;


	WBNDIS_stop( Adapter );
//	OS_SLEEP(10000);

	//======================================================================
	// Release the Rx NDIS resource
	//======================================================================
	if( pWbNdis->NdisBufferPoolOk )// Free NDIS buffer pool handle
		NdisFreeBufferPool( pWbNdis->BufferPoolHandle );

	for( i=0; i<pWbNdis->PacketAllocateCount; i++ ) // Free NDIS packet
		NdisFreePacket( pWbNdis->ReceivePacket[i] );

	if( pWbNdis->NdisPacketPoolOk )
		NdisFreePacketPool( pWbNdis->PacketPoolHandle );// Free NDIS packet pool handle

	OS_SPIN_LOCK_FREE( &pWbNdis->SpinLock );

	OS_TIMER_CANCEL( &pWbNdis->TxRateTimer, &Cancel );
}



BOOLEAN
WBNDIS_Initial( IN PADAPTER Adapter, IN NDIS_HANDLE MiniportAdapterHandle )
{
	PWBNDIS		pWbNdis = &Adapter->WbNdis;
	NDIS_STATUS	status;
	UCHAR		i;

	do
	{
		pWbNdis->MiniportAdapterHandle = MiniportAdapterHandle;// Saving the Adapter Handler
		pWbNdis->connect_status        = NDIS_STATUS_MEDIA_DISCONNECT; // Media disconnect

		OS_SPIN_LOCK_ALLOCATE( &pWbNdis->SpinLock );

		// Allocate ndis packet pool handle
		NdisAllocatePacketPool( &status, &pWbNdis->PacketPoolHandle, ETHERNET_RX_DESCRIPTORS, 16 );
		if( status != NDIS_STATUS_SUCCESS )
			break;
		pWbNdis->NdisPacketPoolOk = 1;

		// Allocate ndis packet
		for( i=0; i<ETHERNET_RX_DESCRIPTORS; i++ )
		{
			NdisAllocatePacket(	&status, &pWbNdis->ReceivePacket[i], pWbNdis->PacketPoolHandle );
			if( status != NDIS_STATUS_SUCCESS)
				break;
			NDIS_SET_PACKET_HEADER_SIZE( pWbNdis->ReceivePacket[i], 14 );
		}
		pWbNdis->PacketAllocateCount = i;
		if( i != ETHERNET_RX_DESCRIPTORS )
			break;

		// Allocate ndis bufer pool
		NdisAllocateBufferPool(	&status, &pWbNdis->BufferPoolHandle, ETHERNET_RX_DESCRIPTORS );
		if( status != NDIS_STATUS_SUCCESS )
			break;
		pWbNdis->NdisBufferPoolOk = 1;

		// Allocate timer for Tx rate show
		pWbNdis->TxRateShowResult = RATE_1M;
		OS_TIMER_INITIAL( &pWbNdis->TxRateTimer, WbNdis_TxRateShow, Adapter );
		OS_TIMER_SET( &pWbNdis->TxRateTimer, 5000 );

		return TRUE;

	}while(FALSE);

	return FALSE;
}

VOID
WBNDIS_ReceivePacket( IN PADAPTER Adapter, IN PRXLAYER1 pRxLayer1 )
{
	PWBNDIS pWbNdis = &Adapter->WbNdis;
	PNDIS_PACKET	pNdisPacket;
	PNDIS_BUFFER	pBufferHandle;
	NDIS_STATUS	BufStatus;
	PUCHAR		BufAddr;
	ULONG		ByteCount = 0;
	USHORT		BufSize;
	UCHAR		BufIndex;
	UCHAR		i;

/*	if( pRxLayer1->BufferNumber == 1 )
	{
		BufIndex = pRxLayer1->BufferQueue[0].BufferIndex;
		BufSize = pRxLayer1->BufferQueue[0].BufferSize;
		BufAddr = pRxLayer1->BufferQueue[0].pBufferAddress;

		NdisMEthIndicateReceive( Adapter->WbNdis.MiniportAdapterHandle,
								 NULL,
								 BufAddr,
								 14,
								 BufAddr + 14,
								 BufSize - 14,
								 BufSize );
		NdisMEthIndicateReceiveComplete( Adapter->WbNdis.MiniportAdapterHandle );
	}
	else
	{*/
	for( i=0; i<pRxLayer1->BufferNumber; i++ )
	{
		BufIndex = pRxLayer1->BufferQueue[i].BufferIndex;
		BufSize = pRxLayer1->BufferQueue[i].BufferSize;
		BufAddr = pRxLayer1->BufferQueue[i].pBufferAddress;
		ByteCount += BufSize;

		NdisAllocateBuffer( &BufStatus,
							&Adapter->WbNdis.ReceiveBuffer[BufIndex],
							Adapter->WbNdis.BufferPoolHandle,
							(PVOID)BufAddr,
							BufSize);

		if( BufStatus == NDIS_STATUS_SUCCESS )
		{
			if( i == 0 )
			{
				pNdisPacket = Adapter->WbNdis.ReceivePacket[BufIndex];//Get the ndis packet handle
			}
			NdisChainBufferAtBack( pNdisPacket, Adapter->WbNdis.ReceiveBuffer[BufIndex] );
		}
	}

	pWbNdis->RxByteCount += ByteCount; // 940803 Add

	// Indicate to NDIS
	NDIS_SET_PACKET_STATUS( pNdisPacket, NDIS_STATUS_RESOURCES );
	NdisMIndicateReceivePacket( Adapter->WbNdis.MiniportAdapterHandle, &pNdisPacket, 1) ;

	// DeAllocate the NDIS buffer
	for( i=0; i<pRxLayer1->BufferNumber; i++ )
	{
		NdisUnchainBufferAtBack(Adapter->WbNdis.ReceivePacket[0], &pBufferHandle);

		if( pBufferHandle ) NdisFreeBuffer(pBufferHandle);
	}
//	}
}


VOID WbNdis_TxRateShow( IN PVOID S1, IN PADAPTER Adapter, IN PVOID S3, IN PVOID S4 )
{
	PWBNDIS	pWbNdis = &Adapter->WbNdis;

	if( CURRENT_LINK_ON )
	{
		if( ((pWbNdis->TxByteCount - pWbNdis->TxByteCount_last) == 0 ) &&
			( Adapter->sLocalPara.TxRateMode == RATE_AUTO ) )
			pWbNdis->TxRateShowResult = psLOCAL->SRateSet[psLOCAL->NumOfSRate - 1]; // Get the maximum of Tx rate; // No traffic
		else
			pWbNdis->TxRateShowResult = psLOCAL->CurrentTxRate;

		pWbNdis->TxByteCount_last = pWbNdis->TxByteCount;
	}
	else
		pWbNdis->TxRateShowResult = RATE_1M;

		// 20060317 The variable maybe set to 0 due to Radio On/Off. Ignore the condition here
//	if( !pWbNdis->shutdown )
		OS_TIMER_SET( &pWbNdis->TxRateTimer, 5000 ); // 5 sec
}

VOID WBNDIS_ConnectStatus( IN PADAPTER Adapter, IN ULONG flag )
{
	Adapter->WbNdis.connect_status = NDIS_STATUS_MEDIA_DISCONNECT;
	if( flag == OS_CONNECTED )
		Adapter->WbNdis.connect_status = NDIS_STATUS_MEDIA_CONNECT;

	NdisMIndicateStatus( Adapter->WbNdis.MiniportAdapterHandle,
						 Adapter->WbNdis.connect_status,
						 NULL, 0 );
	NdisMIndicateStatusComplete( Adapter->WbNdis.MiniportAdapterHandle );
}

#ifdef NDIS50_MINIPORT
VOID WbInitializeString( IN OUT PNDIS_STRING DestinationString, IN PUCHAR SourceString )
{
	USHORT	i, len;

	len = (USHORT)strlen( SourceString );
	if( (OS_MEMORY_ALLOC( (PVOID *)&DestinationString->Buffer, 100 ) != OS_MEMORY_ALLOC_OK) || // 100 byte maximum
		(len >= 50 ) )
	{
		DestinationString->MaximumLength = 0;
		DestinationString->Length = 0;
		return;
	}

	OS_MEMORY_CLEAR( DestinationString->Buffer, 100 );
	DestinationString->MaximumLength = 100/2;
	DestinationString->Length = len*2;
	for( i=0; i<len; i++ )
		DestinationString->Buffer[i] = SourceString[i];
}

VOID WbFreeString( IN NDIS_STRING String )
{
	if( String.Buffer )
		OS_MEMORY_FREE( String.Buffer, 100 );
	String.Buffer = NULL;
}
#endif



